package jgrouse.jgdoc;

import java.io.CharArrayReader;
import java.io.IOException;
import java.io.Reader;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.parser.DTD;
import javax.swing.text.html.parser.DocumentParser;
import javax.swing.text.html.parser.ParserDelegator;

public class HTMLChecker
{
	private int m_line = 0;
	private String m_error = null;
	private static Map<String, String> s_map;
	private static Pattern m_err_re = Pattern.compile("\\W*([\\w\\.]+)\\s*(.*)");
	
	public HTMLChecker()
	{
	}
	
	public boolean validateHTML(String html) throws IOException
	{
		DTD dtd = DTD.getDTD("html32");
		Parser p = new Parser(dtd);
		char[] c = new char[html.length()];
		html.getChars(0, html.length(), c, 0);
		Reader r = new CharArrayReader(c);
		p.parse(r, new Callback(), true);
		return m_error == null;
	}
	
	private void handleError(int line, String pErrString)
	{
    pErrString = pErrString.replaceAll("[\\s\\?]+", " ").trim();
    String err = pErrString, details = "";

    Matcher m = m_err_re.matcher(pErrString);
    if (m.matches()) {
      err = m.group(1);
      details = m.group(2);
    }
    String msg = s_map.get(err);

    // Ignore the error?
    if ("---".equals(msg)) return;

    msg = (msg == null) ? pErrString : msg + " " + details;

    m_line = line;
    m_error = "In description: " + msg + " (err:" + err + ")";
	}
	
	//just has to be there to avoid NPE
	private class Callback extends HTMLEditorKit.ParserCallback
	{
	}
	
	private class Parser extends DocumentParser
	{
		public Parser(DTD pDtd)
		{
			super(pDtd);
		}
		@Override
		protected void handleError(int pLn, String pErrorMsg)
		{
			super.handleError(pLn, pErrorMsg);
			HTMLChecker.this.handleError(pLn, pErrorMsg);
		}
	}
	

	public int getLine()
	{
		return m_line;
	}

	public String getError()
	{
		return m_error;
	}
	
	
	static 
	{
		// force definition of DTD
		new ParserDelegator();

		s_map = new HashMap<String, String>();

    // Errors pulled from http://www.jdocs.com/page/AjaxSourceCode?oid=55407
    // and http://www.irf.se/~ionogram/ionogram/july/900726-03
    // Use "---" to suppress an error
    s_map.put("attvalerr",               "Attribute value error");
    s_map.put("comment.close",           "Unclosed comment");
    s_map.put("delimiter.missing",       "string delimiter missing");
    s_map.put("doctitle",                "HTML Errors");
    s_map.put("end.extra.tag",           "Extra end tag");
    s_map.put("end.missing",             "Missing closing tag");
    s_map.put("end.unexpected",          "End tag not expected");
    s_map.put("end.unrecognized",        "End tag unrecognized");
    s_map.put("eof.comment",             "EOF in comment");
    s_map.put("eof.literal",             "EOF in literal content");
    s_map.put("eof.script",              "---");
    s_map.put("eof",                     "unexpected EOF");
    // s_map.put("exception",               "Exception");
    s_map.put("expected",                "Expected");
    s_map.put("expected.endtagname",     "Missing end tag name");
    s_map.put("expected.tagname",        "Tag name expected");
    s_map.put("ident.expected",          "Identifier expected");
    s_map.put("invalid.commentchar",     "Invalid character in comment");
    s_map.put("invalid.entref",          "Invalid general entity reference");
    s_map.put("invalid.markup",          "Invalid markup");
    s_map.put("invalid.shortend",        "Invalid short end tag");
    s_map.put("invalid.tagatt",          "---");
    s_map.put("invalid.tagattval",       "---");
    s_map.put("invalid.tagattstyle",     "---");
    s_map.put("invalid.tagchar",         "Invalid character");
    s_map.put("ioexception",             "---");
    s_map.put("javascript.unsupported",  "---");
    s_map.put("multi.tagatt",            "Attribute specified more than once");
    s_map.put("req.att ",                "---");
    s_map.put("start.missing",           "---");
    s_map.put("start.missingbody",       "---");
    s_map.put("tag.ignore",              "---");
    s_map.put("tag.unexpected",          "Tag not expected");
    s_map.put("tag.unrecognized",        "Tag unrecognized");
    s_map.put("terminated",              "---");
    s_map.put("unexpected.pcdata",       "---");
    s_map.put("unmatched.endtag",        "Unmatched end tag");
	}
}
